﻿using System;

namespace TimeCalendar
{
    public static class TimeCalendarConverter
    {
        public static GregorianDate FromMayaToGregorian(MayaDate md, Correlations correlation)
        {
            var mayaDays = md.Baktun*144000 + md.Katun*7200 + md.Tun*360 + md.Uinal*20 + md.Kin;
            var julianDay = mayaDays + (int)correlation;
            return FromJulianToGregorian(julianDay);
        }

        public static MayaDate FromGregorianToMaya(GregorianDate gd, Correlations correlation)
        {
            var julianDays = FromGregorianToJulian(gd);
            if (julianDays < (int)correlation)
                throw new Exception("Not right correlation or Gregorian date");
            var mayaDays = julianDays - (int)correlation;
            int baktunRemain;
            var baktun = (byte)Math.DivRem(mayaDays, 144000, out baktunRemain);
            int katunRemain;
            var katun = (byte)Math.DivRem(baktunRemain, 7200, out katunRemain);
            int tunRemain, kin;
            var tun = (byte)Math.DivRem(katunRemain, 360, out tunRemain);
            var uinal = (byte)Math.DivRem(tunRemain, 20, out kin);
            return new MayaDate(baktun, katun, tun, uinal, (byte)kin);
        }

        public static GregorianDate FromJulianToGregorian(int julianDays)
        {
            int dg, db;
            
            var j = julianDays + 32044;
            var g = Math.DivRem(j, 146097, out dg);
            var c = (dg / 36524 + 1) * 3 / 4;
            var dc = dg - (c * 36524);
            var b = Math.DivRem(dc, 1461, out db);
            var a = (db / 365 + 1) * 3 / 4;
            var da = db - (a * 365);
            var y = g * 400 + c * 100 + b * 4 + a;
            var m = (da * 5 + 308) / 153 - 2;
            var d = da - (m + 4) * 153 / 5 + 122;
            
            int mM;
            var yY = y - 4800 + Math.DivRem((m + 2), 12, out mM);
            mM++;
            var dD = d + 1;
            return yY < 0 ? new GregorianDate(Eras.BC, Math.Abs(yY), (byte)mM, (byte)dD) : 
                new GregorianDate(Eras.AD, yY, (byte)mM, (byte)dD);
        }

        public static int FromGregorianToJulian(GregorianDate gd)
        {
            var a = (14 - gd.Month) / 12;
            int y;
            if (gd.Era == Eras.BC) y = (-1)*(gd.Year) + 4800 - a;
            else y = gd.Year + 4800 - a;
            var m = gd.Month + (12 * a) - 3;
            var julianDays = gd.Day + (((153 * m) + 2) / 5) + (365 * y) + (y / 4) - (y / 100) + (y / 400) - 32045;
            return julianDays;
        }
    }
}
